import datetime
import email.message
import getpass
import imaplib2
import random
import re
import time

username = 'swriddle'
email_address = 'pinwheel.test@gmail.com'

debug = False

class Client:
	def __init__(self, server, username, password):
		self.server, self.username, self.password = server, username, password
		self.receivers = []
		self.modules = []
		self.shutdown_flag = False
		self.client = imaplib2.IMAP4_SSL(self.server)
		self.client.login(self.username, self.password)
		print 'Logged in.'
		self.client.select()
		print 'Inbox selected.'
	
	def add_message(self, subject, text):
		new_message = email.message.Message()
		new_message['Subject'] = subject
		new_message['From'] = email_address
		new_message['To'] = email_address
		new_message.set_payload(text + '\n')
		self.client.append('INBOX', '', imaplib2.Time2Internaldate(time.time()), str(new_message))
	
	def add_receiver(self, receiver):
		receivers.append(receiver)
	
	def go(self):
		while 1:
			try:
				self.client.idle()
				print 'Done idling'
				# Check if there are new messages
				self.process_messages()
				if self.shutdown_flag:
					print 'Orderly shutdown requested.'
					break
			except KeyboardInterrupt:
				print 'Let\'s get out of here.'
				break
	
	def new_messages(self):
		while 1:
			typ, msgnums = self.client.search(None, 'UNSEEN')
			for num in (int(x) for x in msgnums[0].split()):
				typ, data = self.client.fetch(num, '(UID INTERNALDATE BODY[HEADER.FIELDS (FROM SUBJECT)])')
				yield Message(data[0])
			break
		# A generator that checks for new messages that meet the criteria we need.
	
	def process_messages(self):
		for message in self.new_messages():
			self.process(message)
			self.set_read(message)
	
	def shutdown(self):
		self.shutdown_flag = True
	
	def process(self, message):
		for module in self.modules:
			for index, starts_with in enumerate(module.subject_starts):
				subject = message.subject.lower()
				if debug:
					print 'Does "%s" start with "%s"?' % (subject, starts_with)
				if subject.startswith(starts_with):
					if debug:
						print 'Yes'
					module.process(subject, index)
					return
				else:
					if debug:
						print 'No'
	
	def set_read(self, message):
		r = self.client.store(str(message.uid), '+FLAGS', r'(\Seen)')
		print 'store() response:', r
	
	
	def print_raw(self):
		typ, msgnums = self.client.search(None, 'UNSEEN')
		for num in (int(x) for x in msgnums[0].split()):
#		for num in msgnums[0].split():		# This works too, but let's make it clear they're numbers
			typ, msgs = self.client.fetch(num, '(UID INTERNALDATE BODY[HEADER.FIELDS (FROM SUBJECT)])')
			print msgs
#			print msgs[0][1].splitlines()
	
	def close(self):
		self.client.close()
		self.client.logout()
	
	def install_module(self, klass):
		self.modules.append(klass(self))

def get_random_file(prefix):
	directory = '/tmp'
	rng = random.Random()
	return open(directory + '/' + prefix + str(rng.randint(0, 10000000)) + '.txt', 'w')

class Handler:
	def __init__(self, director):
		self.director = director
		self.subject_starts = ()
		self.subject_data = None
	
	def process(self, subject, index):
		self.subject_data = subject[len(self.subject_starts[index]):]
		

class Server(Handler):
	def __init__(self, director):
		Handler.__init__(self, director)
		self.subject_starts = ('[server]',)
	
	def process(self, subject, index):
		Handler.process(self, subject, index)
		print 'Server handler triggered.'
		print 'Informational system message. Ignored.'

class Remember(Handler):
	def __init__(self, director):
		Handler.__init__(self, director)
		self.subject_starts = ('remember', 'rem')
		self.notes = []
	
	def _generate_notes_representation(self):
		return '\n'.join(self.notes)
	
	def process(self, subject, index):
		Handler.process(self, subject, index)
		print 'Remember handler triggered.'
		subject = self.subject_data.strip()
		if subject == '':
			self.director.add_message('[server] Things to remember', self._generate_notes_representation())
		elif subject == 'save':
			f = get_random_file('remember')
			f.write(self._generate_notes_representation())
			f.close()
			print 'Written to:', f.name
		else:
			self.notes.insert(0, self.subject_data)

class Remind(Handler):
	def __init__(self, director):
		Handler.__init__(self, director)
		self.subject_starts = ('remind',)
	def process(self, subject, index):
		Handler.process(self, subject, index)
		print 'Remind handler triggered.'
		print 'Remind: \'%s\'' % (self.subject_data,)
		
class Shutdown(Handler):
	def __init__(self, director):
		Handler.__init__(self, director)
		self.subject_starts = ('shutdown',)
	def process(self, subject, index):
		Handler.process(self, subject, index)
		print 'Shutdown handler triggered.'
		self.director.shutdown()

parse_imap_date = re.compile('([0-9]+)-([A-Z][a-z]{2})-([0-9]+) ([0-9]+):([0-9]+):([0-9]+) (.*)')
translate_3char_month = {'Jan':1, 'Feb':2, 'Mar':3, 'Apr':4, 'May':5, 'Jun':6, 'Jul':7, 'Aug':8, 'Sep':9, 'Oct':10, 'Nov':11, 'Dec':12}

parse_from_line = re.compile('(.*) <(.*)>')

def parse_date(date):
	day, month, year, h, m, s, offset = re.match(parse_imap_date, date).groups()
	month = translate_3char_month[month]
	return datetime.datetime(int(year), month, int(day), int(h), int(m), int(s))

def parse_subject_author(lines):
	address = None
	subject = None
	for line in lines.splitlines():
		line = line.strip()
		if line.startswith('Subject: '):
			subject = line[len('Subject: '):]
		elif line.startswith('From: '):
			l = line[len('From: '):]
			try:
				parsed = re.match(parse_from_line, l).groups()
				address = EmailAddress(*parsed)
			except AttributeError:
				# Simple email format, not "alias <address>"
				address = EmailAddress(l, None)
	return subject, address

class EmailAddress:
	def __init__(self, name, address):
		self.name = name
		self.address = address
	
	def __repr__(self):
		return '%s <%s>' % (self.name, self.address)

class Message:
	def __init__(self, x):
		print x
		schema, data = x
		m = re.search('UID ([0-9]+) INTERNALDATE "(.*?)"', schema)
		self.uid = int(m.groups()[0])
		self.date = parse_date(m.groups()[1])
		self.subject, self.author = parse_subject_author(data)
	
	def __repr__(self):
		return '\'%s\', from: %s on %s' % (self.subject, self.author, self.date)

def main():
	username, password = 'pinwheel.test@gmail.com', 'temppass'
	server = 'imap.gmail.com'
	a = Client(server, username, password)
	print 'Welcoming...'
	a.add_message('[server] Welcome', 'Welcome to Pinwheel!')
	print 'Welcomed.'
	a.install_module(Server)
	a.install_module(Remember)
	a.install_module(Remind)
	a.install_module(Shutdown)
	a.go()
	print 'Closing...'
	a.close()

if __name__ == '__main__':
	main()

